package ccpv;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

import population.PopulationInterface;

public class FileManager {

	public enum COMMAND {Input_Type, Input_Hdr, Input_Qty, Input_Year, Ascriptive_Size, Input_Values, BEGIN_DATA, COLUMNS_HDR, COLUMNS_ASCRIPTIVE, COLUMNS_DESCRIPTIVE, COLUMNS_VALUE, COLUMN_QTY, COLUMN_DATE, COLUMN_LOCATION, COLUMN_ASCRIPTIVE};
	public enum INPUT_TYPE {Tuple_Altruism, Ascriptive_Altruism, Group_Altruism, Groups, Ascriptive_Hierarchy};
	
	public static class Action {
		public COMMAND command;
		public String value;
		
		public Action(COMMAND command, String value) {
			this.command = command;
			this.value = value;
		}
	}
	
	

	/**
	 * Custom error for when file format deviates from the expected. 
	 * @author robert
	 *
	 */
	public static class FormatException extends Exception {

		private static final long serialVersionUID = 1L;
		public FormatException (String error){
			super(error);
		}
	}

	
	/**
	 * Parses the bufferedreader looking for commands. Ignores whitespace lines and comments (# at start of line).
	 * @param buff open file being read
	 * @return Action containing a command and an optional value
	 * @throws IOException
	 * @throws IllegalArgumentException
	 */
	public static Action findNextCommand(BufferedReader buff) throws IOException, IllegalArgumentException {
		String line = "";
		String value = "";
		String[] tokens = null;
		COMMAND cmd = null;
		boolean isFound = false;
		while ((!isFound)&&((line = buff.readLine()) != null)) { // short-circuit evaluation important here
			line = line.trim(); // trim whitespace from each end
			if (line.length() == 0) continue; // skip blank lines
			if (line.charAt(0) == '#') continue; // skip comments

			tokens = line.split(":");
			cmd = COMMAND.valueOf(tokens[0].trim());
			if (tokens.length > 1) {
                value = tokens[1].trim();
			} else {
				value = null;
			}
			isFound = true;
		}
		if (isFound) {
			return new FileManager.Action(cmd,value);	
		} else {
			return null;
		}
	}
	
	public static String findNextLine(BufferedReader buff) throws IOException, IllegalArgumentException  {
		String line = null;
		while ((line = buff.readLine()) != null) {
			if (line.length() == 0) continue; // skip blank lines
			if (line.charAt(0) == '#') continue; // skip comments
			break;
		}
		return line;
	}
	
	public static HashMap<COMMAND,String> findCommandList(BufferedReader buff) throws IOException, IllegalArgumentException {
		HashMap<COMMAND,String> actions = new HashMap<COMMAND,String>();
		String line = "";
		String value = "";
		String[] tokens = null;
		COMMAND cmd = null;
		boolean isFound = false;
		while (((line = buff.readLine()) != null)&&(!isFound)) {
			line = line.trim(); // trim whitespace from each end
			if (line.equals("BEGIN_DATA")) break; // end of command lines
			if (line.length() == 0) continue; // skip blank lines
			if (line.charAt(0) == '#') continue; // skip comments

			tokens = line.split(":");
			cmd = COMMAND.valueOf(tokens[0].trim());
			if (tokens.length > 1) {
                value = tokens[1].trim();
			} else {
				value = null;
			}
			actions.put(cmd,value);
		}
		return actions;
		
	}
	
	public static RunTimeSettings csvReader(String filename) {
		BufferedReader buff;
		RunTimeSettings rt = new RunTimeSettings();

		try {
			buff = new BufferedReader(new FileReader(filename));
		System.out.println("File opened.");
				
		Action act = null;
		while((act = findNextCommand(buff)) != null)
		{	
			System.out.println("Act value:" + act.value);
			if (act.command != COMMAND.Input_Type) {
				throw new FileManager.FormatException("File does not start with an Input_Type declaration.");
			}
			
			INPUT_TYPE	type = INPUT_TYPE.valueOf(act.value);
			System.out.println("Input type: '" + type + "' !!!!");
			switch(type) {
			case Tuple_Altruism:
				System.out.println("Importing Tuple_Altruism...");
				importTupleAltruism(buff,rt, false);
				break;
			case Group_Altruism:
				System.out.println("Importing Group_Altruism...");
				importTupleAltruism(buff,rt, true);
				break;
			case Groups:
				System.out.println("Importing Groups...");
				importGroups(buff,rt);
				int[] lengths = rt.getAscriptiveLengths();
				System.out.println("Lengths size:" + lengths.length);
				for (int i = 0; i<lengths.length; i++) {
					System.out.println(lengths[i]);
				}
//				popInt = Population.getNewPopulationInterface(rt.getAscriptiveLengths());// = new Population()
				System.out.println(rt.toString());
				break;
			case Ascriptive_Hierarchy:
				System.out.println("Importing Ascriptive Hierarchy...");
				//importAscriptiveHierarchy(buff, popInt, rt);
				importAscriptiveHierarchy(buff, null, rt);
				break;
			default: 
			
		  }
		}
		System.out.println("Done importing.");
		
		  buff.close();
		  
		//	rt.setPopulationInt(popInt);
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (FormatException e) {
			e.printStackTrace();
		}
		
		return rt;
	}
	
	
	
	private static void importAscriptiveHierarchy(BufferedReader buff, PopulationInterface pop, RunTimeSettings rt) {
		System.out.println("Importing Ascriptive Hierarchy");
		HashMap<COMMAND,String> actions = new HashMap<COMMAND,String>();
		try {
			actions = findCommandList(buff);
		} catch (IllegalArgumentException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		System.out.println("Metadata: ");
		for (COMMAND cmd : actions.keySet()) {
			System.out.println(cmd + " "+ actions.get(cmd));
		}
		int qty = Integer.parseInt(actions.get(COMMAND.Input_Qty));	
		String name = actions.get(COMMAND.COLUMN_ASCRIPTIVE);	
		
		int counter = 0;
		String line = "";
		String data = "";
		while ((counter < qty)&&(line != null)) {
			line = line.trim();
			data = data + "\n" + line;
			
			// End of Loop cleanup
			counter++;
			try {
				line = findNextLine(buff);
			} catch (IllegalArgumentException e) {
				line = null;
			} catch (IOException e) {
				line = null;
			}
			
		}
//		pop.setHierarchicalStructureForAtttribute(rt.getAscriptiveIndex(name), data);
	}

	
	private static void importTupleAltruism(BufferedReader buff, RunTimeSettings rt, boolean isGroup) {
		HashMap<COMMAND,String> actions = new HashMap<COMMAND,String>();
		try {
			actions = findCommandList(buff);
		} catch (IllegalArgumentException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		System.out.println("Metadata: ");
		for (COMMAND cmd : actions.keySet()) {
			System.out.println(cmd + " "+ actions.get(cmd));
		}
		int qty = Integer.parseInt(actions.get(COMMAND.Input_Qty));	
		
		int counter = 0;
		String line = "";
		try {
			line = findNextLine(buff);
		} catch (IllegalArgumentException e1) {
			line = null;
			e1.printStackTrace();
		} catch (IOException e1) {
			line = null;
			e1.printStackTrace();
		}
		while ((counter < qty)&&(line != null)) {
			line = line.trim();
			System.out.println(counter);
			String[] data = line.split(",");
			int num_cols = data.length;
			// so if we get 7 (0..6), then we have 3 ascriptive characteristics
			// 0,1,2 | 3,4,5 | 6
			ArrayList<Integer> chars1 = new ArrayList<Integer>();
			ArrayList<Integer> chars2 = new ArrayList<Integer>();
			
			int lpcount = 0;
			
			int year = Integer.parseInt(data[0]);
			double value = Double.parseDouble(data[num_cols-1]);
			if (!isGroup) {
			  for (int i=1; i< 1+(num_cols-1)/2;i++) {
				  chars1.add(rt.getAscriptiveIndex(lpcount, data[i]));
			  	  lpcount++;
			  }
			  lpcount = 0;
			  for (int i=1+(num_cols-1)/2; i< 1+num_cols ;i++) {
				chars2.add(rt.getAscriptiveIndex(lpcount, data[i]));
			  	lpcount++;				
		  	  }
			  rt.setAltruism(year, utils.Util.ListToIntArray(chars1), utils.Util.ListToIntArray(chars2), value);
			} else {
				System.out.println("Set Altruism: " + year + " " + data[1] + " " + data[2] + " " + value);
				  rt.setAltruism(year, rt.getGroupCharacter(data[1]), rt.getGroupCharacter(data[2]), value);
			}
			
			// Parse data

			
			
			// End of Loop cleanup
			counter++;
			try {
				if (counter < qty) line = findNextLine(buff);
			} catch (IllegalArgumentException e) {
				line = null;
			} catch (IOException e) {
				line = null;
			}
			
		}
		
	}

/*	private static int getAscriptiveIndex(ArrayList<NamedArrayList<String>> data, int index, String value) {
		int result = data.get(index).indexOf(value.trim());
		if (result == -1) {
			data.get(index).add(value.trim());
			result = data.get(index).size()-1;
		}
		return result;
	}
	
	private static int getAscriptiveIndex(ArrayList<NamedArrayList<String>> data, String value) {
		int result = -1;
		for (int i = 0; i<data.size(); i++) {
			if (data.get(i).name.equals(value)) {
				result = i;
				break;
			}
		}
		return result;
	}
	*/
	
	/**
	 * Splits the string by the delimiter and trims whitespace from each substring. 
	 * @param splitter
	 * @param tosplit
	 * @return
	 */
	private static String[] trimSplit(String delimiter, String tosplit) {
		if (tosplit == null) {
			System.out.println("Input string tosplit is null!");
		}
		String[] trimmed = tosplit.split(delimiter);
		System.out.println("TrimSplit call 2: ");
		for (int i = 0; i<trimmed.length; i++) {
			System.out.println("trimsplit: " + i);
			trimmed[i] = trimmed[i].trim();
		}
		return trimmed;
	}
	
	/**
	 * Splits, trims, and converts the strings to integers.
	 * @param delimiter
	 * @param tosplit
	 * @return
	 */
	private static int[] trimSplitConvertInt(String delimiter, String tosplit) {
		System.out.println("TrimSplitInt call: '" + delimiter + "' " + tosplit );

		String[] trimmed = tosplit.split(delimiter);
		int[] result = new int[trimmed.length];
		for (int i = 0; i<trimmed.length; i++) {
			trimmed[i] = trimmed[i].trim();
			result[i] = Integer.parseInt(trimmed[i]);
		}
		return result;
	}
	
	/**
	 * Returns an arraylist of the values in 'values' corresponding to the indexes in 'indexes'.
	 * @param values
	 * @param indexes
	 * @return
	 */
	private static ArrayList<String> getIndexedValues(String[] values, int[] indexes) {
		ArrayList<String> results = new ArrayList<String>();
		for (int i=0; i<indexes.length; i++) {
			results.add(values[indexes[i]]);	
		}	
		return results;
	}
	
	/**
	 * Imports the population groups defined by their ascriptive characteristics and containing additional data fields. These groups are added to the runtime settings. 
	 * @param buff
	 * @param rt
	 */
	public static void importGroups(BufferedReader buff, RunTimeSettings rt) {
		HashMap<COMMAND,String> actions = new HashMap<COMMAND,String>();
		try {
			actions = findCommandList(buff);
		} catch (IllegalArgumentException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		System.out.println("Metadata: ");
		for (COMMAND cmd : actions.keySet()) {
			System.out.println(cmd + " "+ actions.get(cmd));
		}
		int qty = 0;
		boolean parseToEnd = false;
		if (actions.get(COMMAND.Input_Qty) == "TO_END") { 
			parseToEnd = true;
		}
		else {
			qty = Integer.parseInt(actions.get(COMMAND.Input_Qty));	
		}

		String[] header = trimSplit(",",actions.get(COMMAND.COLUMNS_HDR));
		int[] cols_ascriptive = trimSplitConvertInt(",",actions.get(COMMAND.COLUMNS_ASCRIPTIVE));
		int[] cols_descriptive = trimSplitConvertInt(",",actions.get(COMMAND.COLUMNS_DESCRIPTIVE));
		int[] cols_value = trimSplitConvertInt(",",actions.get(COMMAND.COLUMNS_VALUE));
		int col_qty = Integer.parseInt(actions.get(COMMAND.COLUMN_QTY));
		int col_date = Integer.parseInt(actions.get(COMMAND.COLUMN_DATE));
		int col_location = -1; 
		
		if (actions.containsKey(COMMAND.COLUMN_LOCATION)) {
		  col_location = Integer.parseInt(actions.get(COMMAND.COLUMN_LOCATION));
		}
		int year = 0;
		
		ArrayList<Integer> years = new ArrayList<Integer>();
		HashMap<String,int[]> group_names = new HashMap<String,int[]>();
		ArrayList<NamedArrayList<String>> ascr_names = new ArrayList<NamedArrayList<String>>();
		for(String st: getIndexedValues(header, cols_ascriptive)) {
			ascr_names.add(new NamedArrayList<String>(st));
		}
		HashMap<int[],ArrayList<String>> hm = new HashMap<int[],ArrayList<String>>();
		ArrayList<GroupRecord> group_data = new ArrayList<GroupRecord>();
		
		if (actions.containsKey(COMMAND.Input_Year)) {
			  year = Integer.parseInt(actions.get(COMMAND.Input_Year));
			  years.add(year);
		}
			
		
		System.out.println("Meta-data extracted. Processing data.");
		
		
		String line;
		int counter = 0;
//		int location = 0;
		int index = 0;
		String group;
		ArrayList<Double> values = new ArrayList<Double>();
		int[] character;
		
		try {
			line = findNextLine(buff);
		} catch (IllegalArgumentException e) {
			line = null;
			System.out.println("Illegal argument exception");
		} catch (IOException e) {
			line = null;
			System.out.println("IO exception");
		}
		while ((counter < qty)&&(line != null)) {
			line = line.trim();
			values.clear();
			
			
			String[] data = line.split(",");
			// Parse data
			if (!actions.containsKey(COMMAND.Input_Year)) {
				year = Integer.parseInt(data[col_date]);
		    }
		
			if (cols_descriptive.length > 0) {
				group = data[cols_descriptive[0]];
			} else {
				group = "";
			}
			//location = rt.getLocationIndex(data[col_location]);
			
			
			if (!years.contains(year)) {years.add(year);}
			
			character = new int[cols_ascriptive.length];
			for (int i=0; i<cols_ascriptive.length; i++) {
				index = ascr_names.get(i).indexOf(data[cols_ascriptive[i]].trim());
				if (index == -1) {
					ascr_names.get(i).add(data[cols_ascriptive[i]].trim());
					index = ascr_names.get(i).size()-1;
				}
				character[i] = index;
			}
			
			for (int i=0; i<cols_value.length; i++) {
				values.add(Double.parseDouble((data[cols_value[i]])));
			}
			
			if(!group_names.containsKey(group)) {
				System.out.println("Adding group: " + group);
				group_names.put(group.trim(), character);
			}
			System.out.println("Year: " + year);
			group_data.add(new GroupRecord(character, year, Integer.parseInt(data[col_qty]), values));
			
			// End of Loop cleanup
			counter++;
			System.out.println(counter);
			try {
				if (counter < qty) line = findNextLine(buff);
			} catch (IllegalArgumentException e) {
				System.out.println("Illegal argument exception 2");
				line = null;
			} catch (IOException e) {
				System.out.println("IO exception 2");
				line = null;
			}
			
		}
		rt.Initalize(years, group_names, ascr_names, group_data);
	
		System.out.println("End of groups import");
	}
	
	/*
	public void readXmlFile(String filename) {
		DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder;
        Document doc;
		try {
			docBuilder = docBuilderFactory.newDocumentBuilder();
			doc = docBuilder.parse (new File(filename));
			doc.getDocumentElement().normalize();
			String root = doc.getDocumentElement().getNodeName();
			System.out.println("Processing file: "+filename+" with root: "+root+"\n");
			
			NodeList listOfPersons = doc.getElementsByTagName("person");
			//loadSociety(doc);
			//loadGeography(doc);
			//loadDemographics(doc);
			//loadInterventions(doc);
			//loadIdeologies(doc);
			//loadHistory(doc);
			//loadRuntime(doc);
			
		}
		catch (IOException e) {
				e.printStackTrace();
			}
		 catch (SAXException e) {
				e.printStackTrace();
		} 
		 catch (ParserConfigurationException e) {
			e.printStackTrace();
		}
	}
	
	
		
	public void csvWriter(String filename, RunTimeSettings rt) {
		// create file
		// write header comments
		// output groups
		// output altruism
		// output ascriptive hierarchy
		// close file
	}
	*/
	
}
